""" 
Copyright 2009 Texas A&M University
 
Licensed under the Apache License, Version 2.0 (the "License"); you may not use 
this file except in compliance with the License. You may obtain a copy of the 
License at

http://www.apache.org/licenses/LICENSE-2.0
  
Unless required by applicable law or agreed to in writing, software distributed 
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 
CONDITIONS OF ANY KIND, either express or implied. See the License for the 
specific language governing permissions and limitations under the License.
"""

from logging import getLogger
from copy import deepcopy

from persistent import Persistent


from zope.interface import implements
from zope.component import getUtility

import solr

from meercat.ui.plone.interfaces import IMeercatConnectionConfig
from meercat.ui.plone.interfaces import IMeercatConnectionManager

try:
    from threading import local
except ImportError:
    from dummy_threading import local



log = getLogger('meercat.ui.plone.connection')

_locals = local()

def hasLocal(key):
    return hasattr(_locals,key)

def getLocal(key, marker=None):
    return getattr(_locals,key,marker)

def setLocal(key, value):
    return setattr(_locals, key, value)

setLocal('solr',None)
setLocal('rest',None)

def unicodeStringEncoding(value):
    try:
        if isinstance(value, basestring):
            return value.encode('utf-8')
    except UnicodeDecodeError:
        try:
            return value.decode('utf-8').encode('utf-8')
        except:
            return value
    
    return value
        

def unicodeParameterEncoding(params):
    for k,v in params.iteritems():
            if isinstance(v,basestring):
                params[k] = unicodeStringEncoding(v)
            elif isinstance(v, list):
                params[k] = []
                for sv in v:
                    params[k].append(unicodeStringEncoding(sv))
    
    return params
                    

class MeercatConnectionConfig(Persistent):
    """ utility to hold the connection configuration for the meercat server """
    implements(IMeercatConnectionConfig)
    
    def __init__(self):
        self.enabled = False
        self.solr_enabled = False
        self.solr_url = None
        self.solr_ssl_key_file = None
        self.solr_ssl_cert_file = None
        self.solr_timeout = 0
        self.solr_default_parameters = {}
        self.solr_forced_parameters = {}
        self.rest_enabled = False
        self.rest_url = None
        self.rest_timeout = 0
    
    def getId(self):
        """ return a unique id to be used with GenericSetup """
        return 'meercat'


class MeercatConnectionManager(object):
    """ a thread-local connection manager for meercat """
    implements(IMeercatConnectionManager)

    def __init__(self):
        pass
    
    def _getConfig(self):
        return getUtility(IMeercatConnectionConfig)
    
    def enable(self):
        self._getConfig().enabled = True
    
    def disable(self):
        self._getConfig().enabled = False
    
    def configureSolr(self, **kwargs):
        """ set solr configuration, only pass in parameters that should change """
        config = self._getConfig()
        
        for key, value in kwargs.iteritems():
            if hasattr(config, 'solr_%s' % (str(key),)):
                setattr(config, 'solr_%s' % (str(key),), value)
            else:
                raise AttributeError('%s is not a valid solr configuration parameter' % (str(key),))

        self.closeSolrConnection()
    
    def configureRest(self, **kwargs):
        config = self._getConfig()
        
        for key, value in kwargs.iteritems():
            if hasattr(config, 'rest_%s' % (str(key),)):
                setattr(config, 'rest_%s' % (str(key),), value)
            else:
                raise AttributeError('%s is not a valid rest configuration parameter' % (str(key),))
        
        self.closeRestConnection()
    
    def closeSolrConnection(self):
        """ close the current connection, if any """
        log.debug('closing connection')
        conn = getLocal('solr',None)
        if conn is not None:
            conn.close()
            setLocal('solr',None)
    
    def getSolrConnection(self):
        """ returns an existing connection or opens one """
        config = self._getConfig()
        
        if not config.enabled or not config.solr_enabled:
            return None
        
        conn = getLocal('solr',None)
        
        if conn is None and config.solr_url is not None:
            conn = solr.SolrConnection(url=config.solr_url)
            setLocal('solr',conn)
        
        return conn
    
    def closeRestConnection(self):
        """ close the current rest connection, if any """
        log.debug('closing connection')
        conn = getLocal('rest',None)
        if conn is not None:
            conn.close()
            setLocal('rest',None)
    
    def getRestConnection(self):
        """ return an existing rest connection or opens one and returns it """
        return None
    
    def getIdentifierField(self):
        """ return the primary identifier field from SOLR """
        return u'id'
    
    def getSolrVersion(self):
        """ return the version of SOLR from the configured server """
        return 1.3
    
    def _query(self, **params):
        log.debug(u'query parameters: %s' % (unicode(params)))
        conn = self.getSolrConnection()
        results = conn.query(**unicodeParameterEncoding(params))
        if results.numFound > 0:
            results.header['params'] = unicodeParameterEncoding(results.header['params'])
            results.paginator = solr.SolrPaginator(results)
        else:
            log.info('No results found')
        return results
    
    def query(self, **params):
        if not self.getSolrConnection():
            log.warning('The Meercat SOLR connection has not been enabled')
            return None
        
        config = self._getConfig()
        qp = {}
        # Add all default parameters in
        qp.update(config.solr_default_parameters)
        
        # Add user code submitted parameters
        qp.update(params)
        
        # Add forced query parameters
        qp.update(config.solr_forced_parameters)
        
        worked = False
        count = 0
        
        while not worked and count < 3:
            try:
                return self._query(**qp)
            except Exception, e:
                log.warning('Exception while querying solr: %s' % (str(e)))
                count += 1
        
        log.error('Unable to query SOLR')